//
// Created by squbirreland on 2021/11/19.
//

#include "loop_queue.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"

void p_to_next(struct LoopQueue const *loop_queue, void **pp) {
    void *p = *pp + loop_queue->type_size;
    *pp = p == loop_queue->bp_end ? loop_queue->bp_start : p;
}


struct LoopQueue *new_loop_queue(usize type_size, usize cap) {
    if (type_size == 0 || cap == 0)return NULL;
    struct LoopQueue *lq = malloc(sizeof(struct LoopQueue));
    lq->type_size = type_size;
    lq->size = 0;
    lq->cap = cap;
    lq->bp_start = malloc(type_size * cap);
    lq->bp_end = lq->bp_start + (type_size * cap);
    lq->start = lq->bp_start;
    lq->end = lq->start;
    return lq;
}

void free_loop_queue(struct LoopQueue *loop_queue) {
    free(loop_queue->bp_start);
    free(loop_queue);
};

void *loop_queue_pop_first(struct LoopQueue *loop_queue) {
    if (loop_queue->size == 0)return NULL;
    void *r = malloc(loop_queue->type_size);
    memcpy(r, loop_queue->start, loop_queue->type_size);
    --loop_queue->size;
    p_to_next(loop_queue, &loop_queue->start);
    return r;
}

void loop_queue_expansion(struct LoopQueue *loop_queue) {
    void *new_p = malloc(loop_queue->cap * 2 * loop_queue->type_size);
    void *new_index = new_p;
    void *start = loop_queue->start;
    for (usize i = 0; i < loop_queue->size; ++i) {
        memcpy(new_index, start, loop_queue->type_size);
        p_to_next(loop_queue, &start);
        new_index += loop_queue->type_size;
    }
    loop_queue->cap *= 2;
    free(loop_queue->bp_start);
    loop_queue->bp_start = new_p;
    loop_queue->start = new_p;
    loop_queue->end = new_index;
    loop_queue->bp_end = loop_queue->bp_start + (loop_queue->type_size * loop_queue->cap);
}

void loop_queue_push(struct LoopQueue *loop_queue, void const *value) {
    if (loop_queue->size == loop_queue->cap)loop_queue_expansion(loop_queue);
    memcpy(loop_queue->end, value, loop_queue->type_size);
    p_to_next(loop_queue, &loop_queue->end);
    ++loop_queue->size;
}

char *loop_queue_to_json(struct LoopQueue *loop_queue) {
    char *json = malloc(500);
    sprintf(json, "{\"type_size\":%lu,\"bp_start\":\"%p\",\"bp_end\":\"%p\","
                  "\"size\":%lu,\"cap\":%lu,\"start\":\"%p\",\"end\":\"%p\"}",
            loop_queue->type_size,
            loop_queue->bp_start,
            loop_queue->bp_end,
            loop_queue->size,
            loop_queue->cap,
            loop_queue->start,
            loop_queue->end);
    return json;
}

struct Array *loop_queue_to_array(const struct LoopQueue *loop_queue) {
    struct Array *arr = new_array(loop_queue->size, loop_queue->type_size);
    void *p = loop_queue->start;
    for (usize i = 0; i < loop_queue->size; ++i) {
        array_push(arr, p);
        p_to_next(loop_queue, &p);
    }
    return arr;
}